package com.unicdata.ai.demo_2af.common.config;

import com.unicdata.ai.demo_2af.common.entity.User;
import com.unicdata.ai.demo_2af.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import java.util.List;

/**
 * 自定义Realm,处理用户认证和授权
 *
 * @author lhn
 * @date 2025/06/25 16:49
 * @since 1.0.0
 **/
@Slf4j
public class UserRealm extends AuthorizingRealm {

    @Value("${shiro.retry-limit:5}")
    private int retryLimit;

    @Value("${shiro.lock-time:30}")
    private int lockTime;

    @Value("${shiro.password-expiry-time:90}")
    private int passwordExpiryTime;

    private final UserService userService;

    public UserRealm(UserService userService) {
        this.userService = userService;
    }

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        User user = (User) principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        // 添加角色
        List <String> roles = userService.getUserRoles(user.getId());
        authorizationInfo.addRoles(roles);

        // 添加权限
        List<String> permissions = userService.getUserPermissions(user.getId());
        authorizationInfo.addStringPermissions(permissions);

        return authorizationInfo;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();

        // 根据用户名获取用户信息
        User user = userService.getUserByUsername(username);
        if (user == null) {
            throw new UnknownAccountException("账号不存在");
        }

        // 检查账号状态
        if (user.getStatus() != null && user.getStatus() == 0) {
            throw new DisabledAccountException("账号已被禁用");
        }

        // 检查账号是否被锁定
        if (user.getIsLocked() != null && user.getIsLocked() == 1) {
            // 检查是否已经过了锁定时间
            if (user.getLockedTime() != null) {
                long lockedTime = user.getLockedTime().getTime();
                long currentTime = System.currentTimeMillis();
                long lockDuration = lockTime * 60 * 1000; // 锁定时间(分钟)转毫秒

                if (currentTime - lockedTime >= lockDuration) {
                    // 锁定时间已过，解锁账号
                    user.setIsLocked(0);
                    user.setLoginFailCount(0);
                    user.setLockedTime(null);
                    userService.updateById(user);
                } else {
                    // 计算剩余锁定时间
                    long remainLockTime = (lockDuration - (currentTime - lockedTime)) / (60 * 1000);
                    throw new LockedAccountException("账号已被锁定，请" + remainLockTime + "分钟后再试");
                }
            } else {
                throw new LockedAccountException("账号已被锁定，请联系管理员");
            }
        }

        // 检查密码是否过期
        if (user.getPwdUpdateTime() != null) {
            long lastUpdateTime = user.getPwdUpdateTime().getTime();
            long currentTime = System.currentTimeMillis();
            long expiryDuration = passwordExpiryTime * 24 * 60 * 60 * 1000L; // 过期天数转毫秒

            if (currentTime - lastUpdateTime >= expiryDuration) {
                throw new ExpiredCredentialsException("密码已过期，请修改密码");
            }
        }

        if (user.getLastLoginTime() == null) {
            throw new ExpiredCredentialsException("首次登录，请修改密码");
        }

        // 密码匹配的逻辑在CredentialsMatcher中处理
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user, // principal - 身份凭证(登录成功后通过Subject.getPrincipal()获取)
                user.getPassword(), // credentials - 凭证(密码)
                ByteSource.Util.bytes(user.getSalt()), // credentialsSalt - 凭证盐
                getName() // realmName - realm名称
        );

        return authenticationInfo;
    }

    /**
     * 重写setCredentialsMatcher方法，增加登录失败次数限制
     */
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        LimitRetryCredentialsMatcher limitRetryCredentialsMatcher =
                new LimitRetryCredentialsMatcher(credentialsMatcher, userService, retryLimit, lockTime);
        super.setCredentialsMatcher(limitRetryCredentialsMatcher);
    }

    /**
     * 重写方法，保证能正确处理缓存
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken;
    }
}